/*
**			Heroes IV
**	  Copyright 2001, The 3DO Company
**
**      dialog_garrison.cpp
*/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "dialog_garrison.h"

#include "adaptor_handler.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "army.h"
#include "army_dialog_garrison.h"
#include "basic_dialog.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer.h"
#include "bitmap_layer_window.h"
#include "blacksmith_window.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "debug_message.h"
#include "drag_creature.h"
#include "external_string.h"
#include "interface_help.h"
#include "item_help_block.h"
#include "foreign_creature_array_window_controls.h"
#include "interface_help.h"
#include "ownable_garrisonable_adv_object.h"
#include "player.h"
#include "standard_fonts.h"

extern t_button_cache			g_ok_button;
extern t_button_cache			g_divide_button;
extern t_button_cache			g_divide_single_button;
extern t_button_cache const		k_move_garrison_down_button;
extern t_button_cache const		k_move_up_button;
static t_external_string const	k_hotkey_split( "split_creatures.hot_keys" );

t_bitmap_group_cache k_garrison_bitmaps( "dialog.garrison" );

t_dialog_garrison::t_dialog_garrison( t_window* parent )
					: t_window( k_completely_transparent, parent )
{
}

void t_dialog_garrison::init_dialog( t_army* visiting_army, 
									 t_ownable_garrisonable_adv_object * adv_obj, 
									 t_adventure_frame* frame,
									 std::string const& name_text, 
									 std::string const& intro_text )
{
	t_bitmap_layer const*   layer;
	t_window *              window;
	t_screen_point          origin(0,0);
	t_screen_point          point(0,0);
	t_screen_rect           rect;

	m_visiting_army_ptr = visiting_army;
    m_garrison_ptr = adv_obj;
    m_frame = frame;

    // Return if no garrison or visiting army?
	//if ((m_visiting_army_ptr == NULL) && (m_garrison_ptr->empty()))
    //    return;

	m_bitmaps_ptr = k_garrison_bitmaps.get();
	layer = m_bitmaps_ptr->find( "background" );
	window = new t_bitmap_layer_window( layer, origin, this );

    /*
    ** Display the garrison.
    */
    create_garrison_window();

    /*
    ** Text windows.
    */
	t_text_window* text_window_ptr;

    // Title.
	rect = m_bitmaps_ptr->find( "title" )->get_rect() + origin;
	text_window_ptr = new t_text_window( get_font( 20 ), rect, this, name_text, t_pixel_24(0,0,0));
	text_window_ptr->set_center_horizontal();
	text_window_ptr->set_drop_shadow( false );

    // Text.
	rect = m_bitmaps_ptr->find( "text" )->get_rect() + origin;
	text_window_ptr = new t_text_window( get_font( 18 ), rect, this, intro_text, t_pixel_24(0,0,0));
	text_window_ptr->set_center_horizontal();
	text_window_ptr->set_drop_shadow( false );

    /*
    ** Create buttons.
    */
	t_help_block const&		shared_help = get_help_block( "shared" );
	t_button                *button_ptr;
	rect = m_bitmaps_ptr->find( "ok_button" )->get_rect();
	button_ptr = new t_button( g_ok_button.get(), rect.top_left(), this );
	button_ptr->set_click_handler( bound_handler( *this, &t_dialog_garrison::close_click ));
  set_help( button_ptr , shared_help, "ok" );

    /*
    ** Set the position and display the window.
    */
	t_screen_rect parent_rect = get_parent()->get_client_rect();
    
	rect = m_bitmaps_ptr->get_rect();
	rect += (parent_rect.size() - rect.size()) / 2;
	init( rect );
}

void t_dialog_garrison::create_garrison_window()
{
	t_adventure_map &				  map = *m_garrison_ptr->get_map();
	int								  player_number = map.get_player_number();
	t_button_ptr					  move_up_button;
	t_button_ptr					  move_down_button;


	t_screen_point bottom_point;
	t_screen_rect rect = m_bitmaps_ptr->find( "garrison_rings" )->get_rect();

	bottom_point = rect.top_left();
	//bottom_point.y += ((rect.height())/2);
	bottom_point.y += k_creature_top_window_height; //(m_garrison_window_ptr->get_height());
    
    m_visitor_window_ptr = NULL;

    /*
    ** See if we need to create a tempoary visiting army (as a split target)
    */
    if (m_visiting_army_ptr)
	{
		m_no_visiting_army = false;
	}
	else
    {
    	m_no_visiting_army = true;

	    if ( map.get_player().can_create_army() && !m_garrison_ptr->empty() )
        {
			m_visiting_army_ptr = new t_army;
    		m_visiting_army_ptr->get_creatures().clear();
    		m_visiting_army_ptr->set_owner( m_garrison_ptr->get_owner_number() );

			t_adv_map_point	point;
			if (!m_garrison_ptr->find_adjacent_space( point, *m_visiting_army_ptr ))
			{
				m_visiting_army_ptr = NULL;
			}
        }
    }

    /*
    ** Show visiting heroes - show bottom first because of graphic overlap.
    */
	t_creature_array_window::t_layout first_row;
    if (m_visiting_army_ptr)
    {
		// Place the lower row of rings
       	m_visitor_window_ptr = new t_creature_array_window( bottom_point, &m_visiting_army_ptr->get_creatures(),
                                        t_creature_array_window::k_bottom_row, 0, this );


		m_visitor_window_ptr->set_double_click_handler( bound_handler( *this, &t_dialog_garrison::creature_double_click ) );
		first_row = t_creature_array_window::k_top_row;

    t_screen_rect button_rect = m_bitmaps_ptr->find( "split_button" )->get_rect();
		m_split_button = new t_toggle_button( g_divide_button.get(), button_rect.top_left(), this );

		button_rect = m_bitmaps_ptr->find( "move_up" )->get_rect();
		move_up_button = new t_button( k_move_up_button.get(), button_rect.top_left(), this );
		move_up_button->set_click_handler( bound_handler( *this, &t_dialog_garrison::move_up_clicked ) );

		button_rect = m_bitmaps_ptr->find( "move_down" )->get_rect();
		move_down_button = new t_button( k_move_garrison_down_button.get(), button_rect.top_left(), this );
		move_down_button->set_click_handler( bound_handler( *this, &t_dialog_garrison::move_down_clicked ) );
    }
    else
	{
		// Place the background shading overlay to adapt to "single ring" mode
		t_screen_point origin(0,0);
		t_bitmap_layer const* layer = m_bitmaps_ptr->find( "Single_Row_Background" );
		if (layer)					// Conditional can go once resources are in place....  SCR 2/11/02
			new t_bitmap_layer_window( layer, origin, this );

		first_row = t_creature_array_window::k_single_row;

		t_screen_rect button_rect = m_bitmaps_ptr->find( "single_split_button" )->get_rect();
		m_split_button = new t_toggle_button( g_divide_single_button.get(), button_rect.top_left(), this );
	}

	m_split_button->set_click_handler( bound_handler( *this, &t_dialog_garrison::split_clicked ) );

    /*
    ** Show garrison.
    */
	m_garrison_window_ptr = new t_creature_array_window( rect.top_left(), m_garrison_ptr,
										    first_row, 0, this );

	m_garrison_window_ptr->set_double_click_handler( bound_handler( *this, &t_dialog_garrison::creature_double_click ) );

    int visiting_owner = player_number;

    if (m_visiting_army_ptr)
    {
	    m_garrison_window_ptr->link_selection( m_visitor_window_ptr );
		visiting_owner = m_visiting_army_ptr->get_owner_number();

    }

	// Code assumes that either we have the allied restrictions OR the no-remove garrison
	// restrictions but not both. This is fine if the local player visits an ally-owned
	// no-remove garrison (the allied restrictions are always tougher than the no-remove
	// restrictions), but would be a problem if the dialog could appear when an ally
	// visits the local player's no-remove garrison
	assert( visiting_owner == player_number );

	// Handle allied visits
	if (   m_garrison_ptr->get_owner_number() != player_number 
		|| visiting_owner != player_number )
	{
		setup_foreign_creature_array_window_controls( map, m_garrison_window_ptr );
        if (m_visiting_army_ptr)
		    setup_foreign_creature_array_window_controls( map, m_visitor_window_ptr );

		if ( move_down_button )
		{
			move_down_button->enable( m_garrison_ptr->get_owner_number() == player_number );
		}

		if ( move_up_button )
		{
			move_up_button->enable( visiting_owner == player_number );
		}
	}
	else
	{
		// Handle no-remove garrisons 
 		if ( !m_garrison_ptr->can_remove_garrison() )
		{
            if (m_visiting_army_ptr)
            {
    			m_visitor_window_ptr->set_drag_validate_handler( bound_handler( *this, &t_dialog_garrison::check_drag_drop ) );
    			m_visitor_window_ptr->set_split_min_max_handler( bound_handler( *this, &t_dialog_garrison::check_split ) );
            }
			m_garrison_window_ptr->set_drag_validate_handler( bound_handler( *this, &t_dialog_garrison::check_drag_drop ) );
			m_garrison_window_ptr->set_split_min_max_handler( bound_handler( *this, &t_dialog_garrison::check_split ) );

			if ( move_down_button )
			{
				move_down_button->enable( false );
			}
		}
	}

	// Default select first.
    if ( m_visitor_window_ptr && !m_no_visiting_army )
    {
        m_visitor_window_ptr->select_first();
    }
    else
    {
        m_garrison_window_ptr->select_first();
    }

}

void t_dialog_garrison::close_click( t_button* button )
{
	t_window_ptr ref = this;
	bool is_closed = false;

	if (m_visiting_army_ptr)
    {
        // Create an army if no visiting army & visitor window isn't empty.
    	if ( m_no_visiting_army && !m_visiting_army_ptr->get_creatures().empty() ) 
        {
			t_adv_map_point	point;

			if (m_garrison_ptr->find_adjacent_space( point, *m_visiting_army_ptr ))
			{
				close();
				is_closed = true;
			
				t_counted_ptr< t_adventure_map > map = m_frame->get_map();

				m_visiting_army_ptr->place( *map, point );

				m_visiting_army_ptr->update_state();

				m_frame->update_armies();

				if (   !m_visiting_army_ptr->is_graveyard() 
					&& m_visiting_army_ptr->get_owner() != NULL 
					&& !m_visiting_army_ptr->get_owner()->is_computer()) 
				{
					m_frame->select_army( m_visiting_army_ptr );
				}

				m_visiting_army_ptr->trigger_events();

				if ( map->is_game_over() )
				{
					return;
				}
			}
			else
			{
				DEBUG_MESSAGE( "Failed to place a new army from a garrison on the map." );
				m_garrison_window_ptr->merge( m_visitor_window_ptr, true, true, true );
			}
        }
	
		m_frame->update_army( m_visiting_army_ptr );
    }

	if ( !is_closed )
		close();

	m_garrison_ptr->update_state();

	if ( m_visiting_army_ptr )
	{
		t_adventure_map_window* map_window = m_frame->get_map_window();
		map_window->check_enemy_attack( m_visiting_army_ptr );
	}
}

// ---------------------------------------------------------
// Decide if a given drag-drop operation is legal -- in 
// this case, the drag can be illegal if the garrison
// cannot be removed
// ---------------------------------------------------------
void t_dialog_garrison::check_drag_drop( t_creature_array_window::t_drag_drop_validate_data const & data, bool & accept )
{
	// Can drag creatures & artifacts -- but not heroes -- in. Cannot drag anything out
	t_drag_creature const * drag_creature = dynamic_cast< t_drag_creature const * >( data.drag_source );

	if ( drag_creature )
	{
		if ( drag_creature->get_source_window() == data.drag_target )
		{
			// Can always rearrange within an army
			accept = true;
		}
		else if ( drag_creature->get_source_window() == m_garrison_window_ptr )
		{
			accept = false;
		}
		else
		{
			assert( drag_creature->get_source_window() == m_visitor_window_ptr );

			if ( drag_creature->get_data()->get_hero() )
			{
				accept = false;
			}
			else 
			{
				// Don't allow swap-outs
				t_creature_stack const & target_stack = (*data.drag_target->get_army())[data.drag_target_slot];
				
				if ( target_stack.get_number() > 0 && target_stack.get_creature_type() != drag_creature->get_data()->get_creature_type() )
				{
					accept = false;
				}
				else
				{
					accept = true;
				}
			}
		}

		return;
	}

	assert( false ); // Shouldn't be any way of dragging artifacts or other stuff
					 // If there is, we need to implement tests here

	accept = true;
	return;
}

// ---------------------------------------------------------
// Set up splits. Don't allow no-remove creatures to be 
// removed from no-remove garrisons during a split
// ---------------------------------------------------------
void t_dialog_garrison::check_split( t_creature_array_window::t_split_min_max_data & data )
{
	if ( data.left_army == m_garrison_ptr && !m_garrison_ptr->can_remove_garrison() )
	{
		*data.min_left = maximum( 1, (*data.left_army)[data.left_slot].get_number() );
	}

	if ( data.right_army == m_garrison_ptr && !m_garrison_ptr->can_remove_garrison() )
	{
		*data.min_right = maximum( 1, (*data.right_army)[data.right_slot].get_number() );
	}
}


// ---------------------------------------------------------
// Double clicked on a creature. Open army dialog
// ---------------------------------------------------------
void t_dialog_garrison::creature_double_click( t_creature_array_window * window, int slot )
{
	t_counted_ptr< t_army_dialog_garrison > dialog = new t_army_dialog_garrison( m_garrison_ptr, m_visiting_army_ptr, m_frame, window->get_army(), slot );

	dialog->run_modal();

	m_garrison_window_ptr->update();
	if ( m_visitor_window_ptr )
		m_visitor_window_ptr->update();
}

// ---------------------------------------------------------
// Clicked on move garrison to visiting
// ---------------------------------------------------------
void t_dialog_garrison::move_down_clicked( t_button * )
{
	t_adventure_map &				  map = *m_garrison_ptr->get_map();
	int								  player_number = map.get_player_number();
	
	assert( m_garrison_ptr->can_remove_garrison() );
	assert( m_garrison_ptr->get_owner_number() == player_number );
	assert( m_visitor_window_ptr != NULL );

	int								  visiting_owner = m_visiting_army_ptr->get_owner_number();

	m_visitor_window_ptr->merge( m_garrison_window_ptr, true, visiting_owner == player_number, visiting_owner == player_number );
}

// ---------------------------------------------------------
// Clicked on move visiting to garrison
// ---------------------------------------------------------
void t_dialog_garrison::move_up_clicked( t_button * )
{
	t_adventure_map &				  map = *m_garrison_ptr->get_map();
	int								  player_number = map.get_player_number();
	int								  garrison_owner = m_garrison_ptr->get_owner_number();
	bool							  can_remove = m_garrison_ptr->can_remove_garrison();

	m_garrison_window_ptr->merge( m_visitor_window_ptr, true, can_remove && garrison_owner == player_number, can_remove && garrison_owner == player_number );
}

// ---------------------------------------------------------
// Start or end split
// ---------------------------------------------------------
void t_dialog_garrison::split_clicked( t_button * button )
{
	if (button->is_pressed())
	{
		m_garrison_window_ptr->set_divide_mode( bound_handler( *this, &t_dialog_garrison::cancel_divide_mode ));
		if (m_visitor_window_ptr != 0)
		{
			m_visitor_window_ptr->set_divide_mode( bound_handler( *this, &t_dialog_garrison::cancel_divide_mode ));
		}
	}
	else
	{
		cancel_divide_mode();
	}
}


// --------------------------------------------------------------------
// Cancel split
// --------------------------------------------------------------------
void t_dialog_garrison::cancel_divide_mode()
{
	m_split_button->set_pressed( false );
	m_garrison_window_ptr->cancel_divide_mode();
	if (m_visitor_window_ptr != 0)
		m_visitor_window_ptr->cancel_divide_mode();
}

